package com.hmdp.utils;

import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;

/**
 * 基于Redis的分布式ID生成器
 * 功能：生成全局唯一、有序递增的64位Long型ID
 * 设计思路：ID = 时间戳偏移量（高32位） + 当日序列号（低32位）
 */
@Component
public class RedisIdWorker {

    /**
     * 起始时间戳（基准时间）：2022-01-01 00:00:00（UTC）
     * 作用：计算当前时间的偏移量，减少ID整体长度（避免用完整时间戳导致ID过长）
     */
    private static final long BEGIN_TIMESTAMP = 1640995200L;

    /**
     * 序列号占用的位数：32位
     * 限制：每天每个业务前缀最大可生成 2^32-1 个ID（约42亿），满足高并发场景
     */
    private static final int COUNT_BITS = 32;

    /**
     * Redis模板：用于操作Redis的自增命令（保证并发下序列号唯一）
     */
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 构造器注入Redis模板
     */
    public RedisIdWorker(StringRedisTemplate stringRedisTemplate) {
        this.stringRedisTemplate = stringRedisTemplate;
    }

    /**
     * 生成分布式ID
     * @param keyPrefix 业务前缀（如"order"、"user"），用于区分不同业务场景的ID
     * @return 64位唯一有序ID
     */
    public long nextId(String keyPrefix) {
        // 1. 生成时间戳部分（高32位）
        // 1.1 获取当前时间（UTC时区，避免时区差异导致时间计算偏差）
        LocalDateTime now = LocalDateTime.now();
        // 1.2 转换为秒级时间戳（精确到秒，平衡ID精度和生成效率）
        long nowSecond = now.toEpochSecond(ZoneOffset.UTC);
        // 1.3 计算相对于基准时间的偏移量（减少时间戳位数，压缩ID长度）
        long timestamp = nowSecond - BEGIN_TIMESTAMP;

        // 2. 生成序列号部分（低32位）
        // 2.1 按"yyyy:MM:dd"格式化当前日期，作为序列号的分区粒度
        // 目的：1. 避免单key长期自增导致的数值溢出；2. 方便按日期统计ID生成量
        String date = now.format(DateTimeFormatter.ofPattern("yyyy:MM:dd"));
        // 2.2 基于Redis的INCR命令生成当日自增序列号（原子操作，保证并发安全）
        // Redis键格式："icr:业务前缀:日期"（如"icr:order:2023:10:01"）
        long count = stringRedisTemplate.opsForValue().increment("icr:" + keyPrefix + ":" + date);

        // 3. 拼接时间戳和序列号（位运算组合）
        // 时间戳左移32位，腾出低32位空间；通过按位或（|）拼接序列号，形成64位ID
        // 特性：ID天然有序（时间戳递增+序列号递增），可直接用于排序
        return timestamp << COUNT_BITS | count;
    }
}